package AG;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Random;

import FactoriaCromosomas.FactoriaCrom;
import Herramientas.DatosGraficas;
import Herramientas.MyRandom;
import Herramientas.dupla;

public class Agenetico {
	private ArrayList<Cromosoma> pob;
	private int tamPob;
	private int num_max_generaciones;
	private Cromosoma elMejor;
	//private Cromosoma mejorGeneracion;
	private double mediaGeneracion;
	private static int pos_mejor;  //�?�? �porqu� est�tico?
	private double prob_cruce;
	private double prob_mut;
	//private double tolerance;
	private int generacionActual;
	private boolean maximizar;
	private double cmax,cmin;
	private int tamElite;
	//private boolean cruceIntermedio;
	//private int n;
	
	/**
	 * CONSTRUCTURA
	 * 
	 * @param tamPob tama�o de la poblacion
	 * @param numGener numero de generaciones
	 * @param probCruce probabilidad de cruce
	 * @param probMut probabilidad de mutaci�n
	 * @param tolerance tolerancia
	 * @param maximizar true (maximizar) o false (minimizar)
	 * @param cruceIntermedio true (cruce a nivel de bit) o false (a nivel de genes)
	 * @param n (n�mero de genes de la funci�n 4)
	 */
	public Agenetico(int tamPob,int numGener,double probCruce,double probMut, double tolerance,boolean maximizar,boolean cruceIntermedio,int n){
		this.tamPob = tamPob;
		this.num_max_generaciones = numGener;
		this.prob_cruce =  probCruce;
		this.prob_mut = probMut;
		//this.tolerance = tolerance;
		this.generacionActual=0;
		this.maximizar=maximizar;
		//this.cruceIntermedio=cruceIntermedio;
		//this.n =n;
	}
	
	//****************MUTADORAS Y ACCESORAS*****************************
	public ArrayList<Cromosoma> getPob() {
		return pob;
	}
	public void setPob(ArrayList<Cromosoma> pob) {
		this.pob = pob;
	}
	public int getTamPob() {
		return tamPob;
	}
	public void setTamPob(int tamPob) {
		this.tamPob = tamPob;
	}
	public int getNum_max_generaciones() {
		return num_max_generaciones;
	}
	public void setNum_max_generaciones(int num_max_generaciones) {
		this.num_max_generaciones = num_max_generaciones;
	}
	public Cromosoma getElMejor() {
		return elMejor;
	}
	public void setElMejor(Cromosoma elMejor) {
		this.elMejor = elMejor;
	}
	public int getPos_mejor() {
		return pos_mejor;
	}
	public void setPos_mejor(int pos_mejor) {
		this.pos_mejor = pos_mejor;
	}
	public double getProb_cruce() {
		return prob_cruce;
	}
	public void setProb_cruce(double prob_cruce) {
		this.prob_cruce = prob_cruce;
	}
	public double getProb_mut() {
		return prob_mut;
	}
	public void setProb_mut(double prob_mut) {
		this.prob_mut = prob_mut;
	}
//	public double getTolerance() {
//		return tolerance;
//	}
//	public void setTolerance(double tolerance) {
//		this.tolerance = tolerance;
//	}
	public boolean getMaximizar() {
		return maximizar;
	}
	public void setMaximizar(boolean maximizar) {
		this.maximizar = maximizar;
	}
	/*public Cromosoma getMejorGeneracion() {
		return mejorGeneracion;
	}
	public void setMejorGeneracion(Cromosoma mejorGeneracion) {
		this.mejorGeneracion = mejorGeneracion;
	}*/
	public double getMediaGeneracion() {
		return mediaGeneracion;
	}
	public void setMediaGeneracion(double mediaGeneracion) {
		this.mediaGeneracion = mediaGeneracion;
	}
	public void setGeneracionActual(int generacionActual) {
		this.generacionActual = generacionActual;
	}
	public int getGeneracionActual() {
		return generacionActual;
	}
	public double getCmax() {
		return cmax;
	}
	public void setCmax(double cmax) {
		this.cmax = cmax;
	}
	public double getCmin() {
		return cmin;
	}
	public void setCmin(double cmin) {
		this.cmin = cmin;
	}
	public int getTamElite() {
		return tamElite;
	}
	public void setTamElite(int tamElite) {
		this.tamElite = tamElite;
	}
	
	/**
	 * Crea una poblaci�n con el n�mero de individuos del tama�o de la poblaci�n.
	 * Estos individuos ya tienen su genotipo, su fenotipo y su aptitud.
	 * 
	 * @param tipoCromosoma indica la funci�n que se ha escogido, para saber como
	 * 						crear a cada individuo.
	 */
	public void inicializar(double precioKm){
		// para el mejor
		Cromosoma c1 = FactoriaCrom.dameCromosoma(6, 0.001,2,precioKm);
		c1.setAptitud(-Double.MAX_VALUE); // -infinito
		elMejor = c1;
		// Resto
		pob = new ArrayList<Cromosoma>();
		//System.out.println("--- POBLACION INICIAL ---");
		for(int i = 0; i<this.tamPob;i++){
			Cromosoma c = FactoriaCrom.dameCromosoma(6, 0.001,2,precioKm);
			c.inicializarCromosoma();
			c.setFenotipo(((CromosomaCiudades)c).obtenFenotipo());
			c.setAptitud(c.evaluar());
			((CromosomaCiudades) c).setPrecioKilometro(precioKm);
			pob.add(c);
			//System.out.println("  " + i + "       " + c.getAptitud());

		}
		//System.out.println("---------");

	}
	
	/**
	 * Calculamos la posici�n del mejor individuo, y la puntuaci�n y puntuaci�n acumulada de 
	 * cada uno de ellos.
	 * 
	 * aqu� las aptitudes ya est�n transformadas, por lo que me da igual que sea maximizar o minimzar.
	 */
	public void evaluarPoblacion(){
		double punAcu=0.0;
		double aptitudMejor=-Double.MAX_VALUE; // PONGO - Y MAX
		double sumaAptitud=0.0;
		
		Iterator<Cromosoma> it = pob.iterator();
		int i=0;
		// recorro la poblaci�n, voy calculando la suma de las aptitudes y me quedo con la posici�n del mejor 
		while(it.hasNext()){
			Cromosoma c = it.next();
			sumaAptitud += c.getAptitud();
			if(c.getAptitud()> aptitudMejor){
				aptitudMejor = c.getAptitud();
				pos_mejor = i;
				}
			i++;		
		}
		
		// recorro la poblaci�n y calculo la puntuaci�n y la puntuaci�n acumulada
		int j;
		for(j=0;j<tamPob;j++){		
				pob.get(j).setPuntuacion(pob.get(j).getAptitud()/sumaAptitud);
				pob.get(j).setPuntuacion_acumulada(pob.get(j).getPuntuacion()+punAcu);	
				punAcu = punAcu + pob.get(j).getPuntuacion();
		}

		//this.mejorGeneracion = pob.get(pos_mejor);
		
	}
		
/**
 * comprueba si ya han pasado todas las generaciones	
 * @return true si es final, false si no.
 */
public boolean terminado(){
	return generacionActual==num_max_generaciones;
}

/**
 * seleccionamos la poblaci�n, dependiendo del tipo de selecci�n escogida por el usuario
 * @param tipo tipo de selecci�n: ruleta, torneo, estocastico, restos
 */
public void seleccion(String tipo,double presionS,int generacion){
	if(tipo.equals("ruleta")){
		int[] supervivientes = new int[1]; // da igual, no lo voy a usar
		seleccionRuleta(0,supervivientes);
	}
	else if (tipo.equals("torneo")){
		int z = 3; //n�mero de cromosomas que luchar�n en cada pelea. Entre 2 o 3
		seleccionTorneo(0,z);
	}
	else if (tipo.equals("estocasticos")){
		seleccionEstocastica(0);
	}
	else if (tipo.equals("restos")){
		seleccionRestos();
	}
	else if (tipo.equals("ranking")){
		//double presionSelectiva = 1.4;
		seleccionRanking(presionS);
	}
	else if (tipo.equals("propia")){
		seleccionPropia(generacion);
	}
	/*
	 * seleccion por truncamiento
	 * cogemos una copia de la poblaci�n.dividimos su aptitud entre todos por dos. 
	 * y llevamos un int[tamPob] auxiliar, con el numero por el que dividir.
	 * 
	 * repetimos (i=0;i<tamPob;i++)
	 * 		sort(pob)
	 * 		superviviente[i]=posicion(primero); // no es una copia, es saber cual ocupa en la poblacion inicial
	 *		pob.get(primero).setAptitud( pob.get(primero).getAptitud()/auxiliar[posicion(primero)];
	 * 		auxiliar[posicion(primero)]++;
	 */
}

private void seleccionPropia(int generacion){
	
	Collections.sort(pob);   // IMPLEMENTAR EL COMPARE TO!!!!!!
	ArrayList<Cromosoma> pobAux = new ArrayList<Cromosoma>();
	double presionSelectiva = 1 + (double)generacion/this.getNum_max_generaciones();
	// especie de elitismo
	pobAux.add(pob.get(0).copia());// copio el primer elemento
	pobAux.add(pob.get(1).copia());// copio el segundo elemento
	int numElems = 2;
	double[] listaPuntuaciones = rankPopulation(presionSelectiva);
	double puntuacionAcumulada = listaPuntuaciones[listaPuntuaciones.length-1];
	while(numElems< pob.size()){
		double x = (double)(Math.random()*puntuacionAcumulada);
		if(x<=listaPuntuaciones[0]){ // selecciono el primer elemento.
			pobAux.add(pob.get(0).copia());
			numElems++;
		}
		else{
			for(int i=1; i<listaPuntuaciones.length; i++){
				if(x>listaPuntuaciones[i-1] && x<=listaPuntuaciones[i]){ // selecciono el individuo i
					pobAux.add(pob.get(i).copia());
					numElems++;
				}
			}
		}
	}
	
	pob=new ArrayList<Cromosoma>();
	for (int j=0;j<pobAux.size();j++){
		Cromosoma copiaP = pobAux.get(j).copia();
		pob.add(copiaP);
	}
}


/**
 * SELECCION POR RANKING
 * 
 * Selecciona la poblaci�n dependiendo de la posicion que ocupa en el ranking, que es la poblacion ordenada.
 * Dependiendo de la presionSelectiva que se le pase, tendr� m�s probabilidad de coger a los altos que a los bajos.
 * Con presionSelectiva = 1 => Tenemos la misma posibilidad de coger cualquier individuo.
 * Con presionSelectiva = 2 => Tenemos el doble de opciones de coger el mejor que el individuo medio.
 */
private void seleccionRanking(double presionSelectiva){
	
	Collections.sort(pob);   // IMPLEMENTAR EL COMPARE TO!!!!!!
	ArrayList<Cromosoma> pobAux = new ArrayList<Cromosoma>();
	
	// especie de elitismo
	pobAux.add(pob.get(0).copia());// copio el primer elemento
	pobAux.add(pob.get(1).copia());// copio el segundo elemento
	int numElems = 2;
	double[] listaPuntuaciones = rankPopulation(presionSelectiva);
	double puntuacionAcumulada = listaPuntuaciones[listaPuntuaciones.length-1];
	while(numElems< pob.size()){
		double x = (double)(Math.random()*puntuacionAcumulada);
		if(x<=listaPuntuaciones[0]){ // selecciono el primer elemento.
			pobAux.add(pob.get(0).copia());
			numElems++;
		}
		else{
			for(int i=1; i<listaPuntuaciones.length; i++){
				if(x>listaPuntuaciones[i-1] && x<=listaPuntuaciones[i]){ // selecciono el individuo i
					pobAux.add(pob.get(i).copia());
					numElems++;
				}
			}
		}
	}
	
	pob=new ArrayList<Cromosoma>();
	for (int j=0;j<pobAux.size();j++){
		Cromosoma copiaP = pobAux.get(j).copia();
		pob.add(copiaP);
	}
}
	

/**
 * Calcula la probabilidad de que cada individuo en el ranking sea elegido.
 * 
 * @param presionSelectiva   probabilidad de que los mejores sean elegidos sobre los promedios.
 * @return   las probabilidades de cada individuo.
 */
private double[] rankPopulation(double presionSelectiva){
	double[] listaProbabilidades= new double[this.getTamPob()]; // Array con las probabilidades de seleccion.
	for(int i=0 ; i<listaProbabilidades.length ; i++){
		int tamanyo = this.getTamPob();
		double probabilidadElemI = (double)(i)/tamanyo; // no se si hace falta el -1 en los dos o en ninguno.
		probabilidadElemI = probabilidadElemI*2*(presionSelectiva-1);
		probabilidadElemI = presionSelectiva - probabilidadElemI;
		probabilidadElemI = (double)probabilidadElemI*((double)1/this.getTamPob());
		if(i!=0)
			listaProbabilidades[i] = listaProbabilidades[i-1] + probabilidadElemI;
		else
			listaProbabilidades[i] = probabilidadElemI;
	}
	return listaProbabilidades;
}

public void seleccionRestos(){
	int[] supervivientes = new int[pob.size()];
	int posSuperviviente=0;
	//int totalCopias=0;
	// k es la poblacion.
	for (int i =0 ; i<pob.size();i++){
		int numCopias=(int)(pob.size()*pob.get(i).getPuntuacion()); //pi*k // OJO!!! ERROR!!!!
		for (int j=0; j<numCopias;j++){
			supervivientes[posSuperviviente]=i;
			posSuperviviente++;
		}
	}
	// el resto, de momento, siempre con ruleta.
	seleccionRuleta(posSuperviviente,supervivientes); 
}

public void seleccionEstocastica(int inicio){
	double prob;
	double a;
	int[] supervivientes = new int[tamPob];
	int posSuperviviente=0;
	
	//System.out.println("--- poblaci�n seleccionada ---");
	// Se genera un n�mero aleatorio, y para cada posicion de la poblacion, se selecciona
	// el individuo en el que caiga el n�mero de la f�rmula en su intevalo de puntuaci�n acumulada
	prob = Math.random(); // �nico randoom.
	for(int i =inicio ; i< tamPob; i++){
		a=(prob+i)/tamPob;
		posSuperviviente=0;
		while(a>pob.get(posSuperviviente).getPuntuacion_acumulada() && (posSuperviviente<tamPob)){
			posSuperviviente++;
		}
		supervivientes[i]=posSuperviviente;
	}
	//System.out.println();
	
	ArrayList<Cromosoma> pobAux = copiar( pob);
	// en pobAux tenemos la poblaci�n copiada. Ahora hay que modificar la poblaci�n
	pob=new ArrayList<Cromosoma>();
	for (int i = 0; i<tamPob; i++){
		Cromosoma copiaP = pobAux.get(supervivientes[i]).copia();
		pob.add(copiaP);
		//System.out.println("  " + supervivientes[i] + "     " + pob.get(i).getFenotipo() + "        " + pob.get(i).getAptitud());
	}
}

/**
 * Selecciono los individuos de la poblaci�n original. Al final, queda el mismo n�mero de individuos.
 * Repito (tamPob) veces una lucha, entre z luchadores, y el mejor se queda en la poblaci�n.
 * 
 * @param inicio // n�mero de individuos ya seleccionados, y desde donde empezamos a seleccionar.
 * @param z // n�mero de individuos en cada lucha
 */
private void seleccionTorneo(int inicio,int z) {
	int[] supervivientes = new int[tamPob];
	//System.out.println("---- poblacion seleccionada por torneo -------");
	
	for (int i=inicio;i< tamPob; i++ ){ // para cada posici�n a seleccionar
		// primero, selecciono los elementos que van a combatir
		int ganador=0;
		double puntuacionGanador=-Double.MAX_VALUE;
		// escojo los luchadores.
		for (int j=0;j<z;j++){
			double aux = Math.random()*(tamPob);
			int luchador = (int) Math.floor(aux);
			// gana el mejor
			if (pob.get(luchador).getAptitud()>puntuacionGanador){
				ganador=luchador;
				puntuacionGanador=pob.get(luchador).getAptitud();
			}
		}
		supervivientes[i]=ganador;
	}
	
	ArrayList<Cromosoma> pobAux = copiar( pob);
	// en pobAux tenemos la poblaci�n copiada. Ahora hay que modificar la poblaci�n original
	pob=new ArrayList<Cromosoma>();
	for (int i = 0; i<tamPob; i++){
		Cromosoma copiaP = pobAux.get(supervivientes[i]).copia();
		pob.add(copiaP);
		//System.out.println("En la posicion  " + i + "  ha ganado: " + supervivientes[i] + "   con una aptitud de:  " + pob.get(i).getAptitud());
	}
	//System.out.println("---------------------------");
}

/**
 * Selecciono los individuos de la poblaci�n original. Al final, queda el mismo n�mero de individuos.
 * En ruleta, hago un random, y el n�mero que coincida en el intervalo de las puntuaciones acumuladas con el 
 * individuo es el que escojo. L�gicamente, los que tienen m�s puntuaci�n tienen m�s opciones de salir
 * @param inicio // n�mero de individuos ya seleccionados, y desde donde empezamos a seleccionar.
 * @param supervivientes // individuos ya seleccionados por restos. Ahora quedan por seleccionar los dem�s (Desde inicio a tamPob)
 */
private void seleccionRuleta(int inicio,int[] supervivientes){
	double prob;
	//int[] supervivientes = new int[tamPob];
	if (inicio==0)
		supervivientes = new int[tamPob];
	int posSuperviviente=0;
	//System.out.println("--- poblaci�n seleccionada ---");
	// para cada posici�n de la poblaci�n, genero un n�mero, y en el intervalo de puntuaciones
	// acumuladas que est�, cojo el individuo y le paso a la nueva poblaci�n.
	for(int i =inicio ; i< tamPob; i++){
		prob = Math.random();
		posSuperviviente=0;
		while(prob>pob.get(posSuperviviente).getPuntuacion_acumulada() && (posSuperviviente<tamPob)){
			posSuperviviente++;
		}
		
		supervivientes[i]=posSuperviviente;
	}
	//System.out.println();
	
	ArrayList<Cromosoma> pobAux = copiar( pob);
	// en pobAux tenemos la poblaci�n copiada. Ahora hay que modificar la poblaci�n
	pob=new ArrayList<Cromosoma>();
	for (int i = 0; i<tamPob; i++){
		Cromosoma copiaP = pobAux.get(supervivientes[i]).copia();
		pob.add(copiaP);
		//System.out.println("  " + supervivientes[i] + "     " + pob.get(i).getFenotipo() + "        " + pob.get(i).getAptitud());
	}
}
	
/**
 * Crea una copia de una poblaci�n de cromosomas.
 * @param pob2 poblacion a copiar
 * @return poblacion copiada
 */
private ArrayList<Cromosoma> copiar(ArrayList<Cromosoma> pob2) {
	ArrayList<Cromosoma> copiada= new ArrayList<Cromosoma>();
	Iterator<Cromosoma> it = pob2.iterator();
	while(it.hasNext()){
		Cromosoma c = it.next();
		Cromosoma CromosomaAux = c.copia();
		copiada.add(CromosomaAux);
	}
	return copiada;
}

/**
 * Recorremos la poblaci�n, y para cada individuo, hacemos un random, y si es mayor que
 * la probabilidad de cruce, pasa a ser padre o madre. Si no, no se reproduce.
 * Despu�s, juntamos una parte del padre y otra de la madre para cada hijo, y se intercambian 
 * en la poblaci�n.
 */
public void reproduccion(int tipoReproduccion,double preciokm){
	// Array seleccionados para reproducir
	int[] reproductores = new int[tamPob];
	int num_seleccion_cruce=0;
	int punto_cruce;
	double prob;
	
	//System.out.print("-- se van a reproducir: --");
	// escojo a los padres y a las madres
	for(int i = 0; i< tamPob ; i++){
		prob = Math.random();
		if(prob < prob_cruce){
			reproductores[num_seleccion_cruce]=i;
			num_seleccion_cruce++;
			//System.out.print(i + " ; " );
		}
	}
	// Hacemos par el numero de indivuos a realizar el coito
	if(num_seleccion_cruce % 2 == 1){
		num_seleccion_cruce--;
		
	}
	
	// punto_cruce es el bit en el que se van a cambiar en caso de ser a nivel de bits. Si no, calculamos el gen al que pertenece ese bit
	/*
	 * aqu� vendr�a un bucles desde 0 hasta NUMEROPUNTOSDECRUCE, que
	 * me genera un n�mero aleatorio, y lo guarda en un Array de n�meros
	 * llamado Cruces, que luego se pasa a crucePunto.
	 * IMPORTANTE: meter antes de esto, el 0 a Cruces, y despu�s, la Longitud del Cromosoma-1 
	 */
	ArrayList<Integer> Cruces = new ArrayList<Integer>();
	Random m = new Random();
	int index1 = m.nextInt(pob.get(0).getGenes().get(0).getAlelos().size());
	int index2=index1;
	while(index1==index2)
		index2=m.nextInt(pob.get(0).getGenes().get(0).getAlelos().size());
	if(index1>index2){
		int aux=index2;
		index2=index1;
		index1=aux;
	}
	Cruces.add(index1);
	Cruces.add(index2);
	//System.out.println("ANTES DEL CRUCE" );
	
	
	
	for(int i =0;i<num_seleccion_cruce;i=i+2){
		dupla Hijos = CromosomaCiudades.reproduce((CromosomaCiudades)pob.get(reproductores[i]),(CromosomaCiudades) pob.get(reproductores[i+1]), Cruces,tipoReproduccion);
		//System.out.println("El padre  " + reproductores[i] + "con apitud: "+ pob.get(reproductores[i]).getAptitud() + " ha sido sustituido por su hijo, que tiene aptitud:" + Hijos.getHijo1().getAptitud());
		//System.out.println("La madre  " + reproductores[i+1] + "con apitud: "+ pob.get(reproductores[i+1]).getAptitud() + " ha sido sustituido por su hija, que tiene aptitud:" + Hijos.getHijo2().getAptitud());
		pob.set(reproductores[i],Hijos.getHijo1());
		pob.set(reproductores[i+1], Hijos.getHijo2());
		
	}
	

	
}

/**
 * Cambia el valor del bit que tiene que ser mutado.
 * Recorro todos los individuos, y todos los bits, y si el random me da un valor
 * menor que el de probabilidad de mutaci�n, muto ese bit.
 */
public void mutacion(int tipoMutacion){
	boolean mutado =false;
	int i,j;
	double prob;
	//System.out.println("Los que han mutado han sido: ");
	//System.out.println("ANTES DE MUTACION");
	for(i=0;i<tamPob;i++){ // para cada individuo
		prob=Math.random();
		mutado=false;
				
				if(prob< prob_mut){ // muta
						CromosomaCiudades mutadoNuevo = CromosomaCiudades.muta((CromosomaCiudades)pob.get(i),tipoMutacion);
						mutado = true;
						//System.out.print("el individuo " + i + " con aptitud:" + pob.get(i).getAptitud());
						pob.set(i, mutadoNuevo);
						
				}
				
				if(mutado){ // si un individuo ha sido mutado, recalculo su fenotipo y su aptitud
					pob.get(i).setFenotipo(((CromosomaCiudades)pob.get(i)).obtenFenotipo());
					pob.get(i).setAptitud(pob.get(i).evaluar());
					/*if (maximizar==false)
						//minimizar
						pob.get(i).setAptitud(cmax-pob.get(i).evaluar());
					
					else
						pob.get(i).setAptitud(pob.get(i).evaluar()-cmin);*/
				
					//System.out.println("  ha sido mutado por un individuo con aptitud:" + pob.get(i).getAptitud());
				}	
		
	}
	//System.out.println("DESPUES DE MUTACION");
/*	for(int z=0;z<tamPob;z++){
		boolean c =CromosomaCiudades.hayRepetidos((CromosomaCiudades)pob.get(z));
		if(c) System.out.println("OMGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
	}*/
	//System.out.println("--------------------");
}

/**
 * Crea los hijos de la reproducci�n.
 * @param padre individuo que crea descendiente
 * @param madre individuo que crea descendiente
 * @param puntoCruce bit por el que se va a producir el cruce (o bit del que sacar el gen por el que se va a cruzar) 
 * @return dupla de cromosomas con los dos hijos creados.
 */
public dupla crucePunto(Cromosoma padre, Cromosoma madre, ArrayList<Integer> Cruces){
	Cromosoma hijo=null;
	Cromosoma hija=null;
	
	// inicializamos a los hijos, dependiendo del tipo de funci�n
	/*if(padre instanceof cromosomaFuncion1){
		hijo = FactoriaCrom.dameCromosoma(1,tolerance,this.n);
		hija = FactoriaCrom.dameCromosoma(1,tolerance,this.n);
		hijo.inicializarCromosoma();
		hija.inicializarCromosoma();
	}
	else{
		if(padre instanceof cromosomaFuncion2){
			hijo = FactoriaCrom.dameCromosoma(2,tolerance,this.n);
			hija = FactoriaCrom.dameCromosoma(2,tolerance,this.n);
			hijo.inicializarCromosoma();
			hija.inicializarCromosoma();
		}
		else if(padre instanceof cromosomaFuncion3){
			hijo = FactoriaCrom.dameCromosoma(3,tolerance,this.n);
			hija = FactoriaCrom.dameCromosoma(3,tolerance,this.n);
			hijo.inicializarCromosoma();
			hija.inicializarCromosoma();
		}
		else if(padre instanceof cromosomaFuncion4){
			hijo = FactoriaCrom.dameCromosoma(4,tolerance,this.n);
			hija = FactoriaCrom.dameCromosoma(4,tolerance,this.n);
			hijo.inicializarCromosoma();
			hija.inicializarCromosoma();
		}
		else if(padre instanceof cromosomaFuncion5){
			hijo = FactoriaCrom.dameCromosoma(5,tolerance,this.n);
			hija = FactoriaCrom.dameCromosoma(5,tolerance,this.n);
			hijo.inicializarCromosoma();
			hija.inicializarCromosoma();
		}
	}
	
	if (cruceIntermedio==true){ // cruce a nivel de bits
		
		 * aqu� habr� que hacer algo as� (cruces es un array con los puntos de cruce, Y A�ADO AL PRINCIPIO 0 Y AL FINAL LONGITUDCROMOSOMA)
		 * 			Cruces tiene que venir ordenado!!
		 * for (int k=0; k<Cruces.size()-1;k++)
		 * inicio = Cruces.get(k);
		 * fin = Cruces.get(k+1);
		 * if (k mod 2 = 0)
		 *    --- hijo = padre, hija = madre  (primer for, desde inicio hasta fin)
		 * else
		 * 	  --- hijo = madre, hijo = padre  (segundo for, desde inicio hasta fin) 
		 
		 
		 for(int k=0; k<(Cruces.size()-1);k++){
			
			int inicio=Cruces.get(k);
			int fin=Cruces.get(k+1);
			if (k % 2 == 0){
				for(int i = inicio; i< fin; i++){
					int tamAcumulado = 0;
					for(int j =0; j<hijo.dameGen(i);j++){
						tamAcumulado+=((Gen) (hijo.getGenes().get(j))).getTamanoGen();  // calculo los bits de los genes anteriores al actual, para saber el bit del gen en el que estoy y tengo que asignar
					}
						// pongo bit a bit al hijo lo del padre, y a la hija lo de la madre
						hijo.getGenes().get(hijo.dameGen(i)).getAlelos().set(i-tamAcumulado, padre.getGenes().get(hijo.dameGen(i)).getAlelos().get(i-tamAcumulado)); 
						hija.getGenes().get(hijo.dameGen(i)).getAlelos().set(i-tamAcumulado, madre.getGenes().get(hija.dameGen(i)).getAlelos().get(i-tamAcumulado));
				}
			}
			else{
				for(int i = inicio; i< fin; i++){
					int tamAcumulado = 0;
					for(int j =0; j<hijo.dameGen(i);j++){
						tamAcumulado+=((Gen)hijo.getGenes().get(j)).getTamanoGen(); // calculo los bits de los genes anteriores al actual, para saber el bit del gen en el que estoy y tengo que asignar
					}
					// pongo bit a bit al hijo lo de la madre, y a la hija lo del padre
					hijo.getGenes().get(hijo.dameGen(i)).getAlelos().set(i-tamAcumulado, madre.getGenes().get(hijo.dameGen(i)).getAlelos().get(i-tamAcumulado));
					hija.getGenes().get(hijo.dameGen(i)).getAlelos().set(i-tamAcumulado, padre.getGenes().get(hija.dameGen(i)).getAlelos().get(i-tamAcumulado));	
				}
			}	
		}	
	}
	else{ // cruce a nivel de genes
		
		 * aqu� habr� que hacer algo as� (cruces es un array con los puntos de cruce, Y A�ADO AL PRINCIPIO 0 Y AL FINAL LONGITUDCROMOSOMA)
		 * 			Cruces tiene que venir ordenado!!
		 * for (int k=0; k<Cruces.size()-1;k++)
		 * inicio = Cruces.get(k);
		 * fin = Cruces.get(k+1);
		 * if (k mod 2 = 0)
		 *    --- hijo = padre, hija = madre  (primer for, desde inicio hasta fin)
		 * else
		 * 	  --- hijo = madre, hijo = padre  (segundo for, desde inicio hasta fin) 
		 
		//int hastaGen=padre.dameGen(puntoCruce);
		for(int k=0; k<(Cruces.size()-1);k++){
			int inicio=Cruces.get(k);
			int fin=Cruces.get(k+1);
			if (k % 2 == 0){
			// pongo gen a gen al hijo lo del padre, y a la hija lo de la madre
				for (int i = inicio; i <fin; i++){
					hijo.getGenes().set(i, padre.getGenes().get(i));
					hija.getGenes().set(i, madre.getGenes().get(i));
				}
			}
			else{
				// pongo gen a gen al hijo lo de la madre, y a la hija lo del padre
				for (int j=inicio; j<fin;j++ ){
					hijo.getGenes().set(j, madre.getGenes().get(j));
					hija.getGenes().set(j, padre.getGenes().get(j));
				}
			}
		}
	}
	
	// calculo fenotipos y aptitudes de los hijos. Puntuaciones no hace falta!, porque se har� en la siguiente fase en evaluar
	hijo.setFenotipo(hijo.fenotipo());
	hija.setFenotipo(hija.fenotipo());
	if(maximizar==false){
		// minimizar
		hijo.setAptitud(cmax-hijo.evaluar());
		hija.setAptitud(cmax-hija.evaluar());
	}
	else{
		hijo.setAptitud(hijo.evaluar()-cmin);
		hija.setAptitud(hija.evaluar()-cmin);
	}	*/
	dupla d  = new dupla();
	d.setHijo1(hijo);
	d.setHijo2(hija);
	return d;
	
}

/**
 * METODO DE TRANSFORMACI�N
 * transforma las aptitudes m�nimas negativas a las m�ximas positivas, para que
 * no haya diferencia entre minimizar y maximizar en las funciones de
 * selecci�n, mutaci�n, reproducci�n....
 */
private void revisarAdaptacionMinimizar() {
	cmax=-(Double.MAX_VALUE);
	// obtengo la aptitud m�xima
	for(int i=0;i<pob.size();i++){
		if(cmax<pob.get(i).getAptitud())
			cmax=pob.get(i).getAptitud();
	} 
	
	if (cmax>0){ //aumento un poco su valor para que despu�s no haya ninguna aptitudo con valor 0
		cmax=cmax*1.05;
	}
	else{
		cmax=cmax*0.95;
	}
	
	//System.out.println("La poblacion transformada ha quedado as�: ");
	// transformo 
	for(int j=0;j<pob.size();j++){
		pob.get(j).setAptitud(cmax-pob.get(j).getAptitud());
		//System.out.println(j + " tiene una aptitud de: " + pob.get(j).getAptitud());
	}
}

/**
 * METODO DE DESPLAZAMIENTO
 * desplaza las aptitudes para que no haya ninguna negativa, y despu�s no haya
 * problemas con la suma de las aptitudes y dem�s.
 */
private void revisarAdaptacionMaximizar() {
	cmin=Double.MAX_VALUE;
	// busco la aptitud m�nima
	for(int i=0;i<pob.size();i++){
		if(cmin>pob.get(i).getAptitud())
			cmin=pob.get(i).getAptitud();
	}
	
	// si hay alguno negativo, tengo que hacer desplazamiento
	if (cmin<0){ 
		for(int j=0;j<pob.size();j++){
			pob.get(j).setAptitud(pob.get(j).getAptitud()-cmin);
		}
	}
	else{
		cmin=0;
	}
}

/**
 * REVERTIR LA TRANSFORMACI�N
 * vuelve a los valores anteriores de la aptitud. Revierte la transformaci�n.
 * Calcula la media de la generacion
 */
private void revertirRevisarAdaptacionMinimizar() {
	double sumaAptitud=0;
	int pm=-1;
	double mejorAptitud=Integer.MAX_VALUE;
	for(int i=0;i<tamPob;i++){
		//pob.get(i).setAptitud(cmax-pob.get(i).getAptitud());
		pob.get(i).setAptitud(pob.get(i).evaluar());
		if (mejorAptitud>pob.get(i).getAptitud()){
			mejorAptitud=pob.get(i).getAptitud();
			pm=i;
		}
		sumaAptitud+=pob.get(i).getAptitud();
	}
	this.mediaGeneracion=sumaAptitud/tamPob;
	this.pos_mejor=pm;
	//System.out.println("media Generacion:  " + this.mediaGeneracion );
	//System.out.println("mejor: " + pm + " con aptitud de: " + pob.get(pm).getAptitud());
}

/**
 * REVERTIR EL DESPLAZAMIENTO
 * vuelve a los valores anteriores de la aptitud. Revierte el desplazamiento
 */
private void revertirRevisarAdaptacionMaximizar() {
	double sumaAptitud=0;
	for(int i=0;i<pob.size();i++){
		//pob.get(i).setAptitud(pob.get(i).getAptitud()+cmin);
		pob.get(i).setAptitud(pob.get(i).evaluar());
		sumaAptitud+=pob.get(i).getAptitud();
	}
	this.mediaGeneracion=sumaAptitud/tamPob;
}

/**
 * ordena los cromosomas por su aptitud, y cogemos los mejores, que ser�n la �lite, 
 * para que pasen directamente a la siguiente fase, y guardar los mejores.
 * La �lite no se excluye de la poblaci�n, por lo que estar�n en los m�todos de
 * selecci�n, reproducci�n y  mutaci�n.
 * 
 * @param tam tama�o de la �lite
 * @return un arrayList con los mejores individuos de la poblaci�n, la Elite
 */
public ArrayList<Cromosoma> seleccionaElite(int tam)
{
	ArrayList<Cromosoma> aux = new ArrayList<Cromosoma>();
	 Collections.sort(this.getPob());
	 for(int i=0;i<tam;i++){
		aux.add(this.getPob().get(i).copia());		
	 }
	return aux;
}

/**
 * Vuelve a incluir a los mejores, que estaban en la �lite, en lugar de los peores.
 * 
 * @param elite arrayList con la �lite que se hab�a copiado al principio de la generaci�n
 */
private void incluye(ArrayList<Cromosoma> elite) {
	this.getPob().addAll(elite);
	Collections.sort(this.getPob());
	for(int i =0;i<this.getTamElite();i++){
		this.getPob().remove(pob.size()-1);
	}
	//System.out.println(pob.size() + " TAMA�O REAL");
	//System.out.println(this.getTamPob() + "TAma�o ideal");
}

public String dameRecorrido(){
	return ((CromosomaCiudades)this.elMejor).dameRecorrido();
}


// Agenetico.ejecuta(elitismo, Integer.parseInt(poblacion), Integer.parseInt(numGeneraciones), pc, pm,tipoSeleccion,tipoMutacion,precioKm);
public static DatosGraficas ejecuta(boolean elitismo,int poblacion,int nGen,double pc,double pm,int tipoSeleccion,int tipoMutacion,int tipoCruce,double precioKm,double presionS){
	Double mejorPuntuacionGlobal;
	ArrayList<Cromosoma> elite=null;
	String tipofunSeleccion = "torneo";
        // TRATAR
	if(tipoSeleccion==1){
		tipofunSeleccion="ruleta";
	}
	else if (tipoSeleccion==2){
		tipofunSeleccion="torneo";
	}
	else if (tipoSeleccion==3){
		tipofunSeleccion="estocasticos"; 
	}
	else if (tipoSeleccion==4){
		tipofunSeleccion="restos";
	}
	else if (tipoSeleccion==5){
		tipofunSeleccion="ranking";
	}
	else if (tipoSeleccion==6){
		tipofunSeleccion="propia";
	}
	ArrayList<Double> listaMejoresGen = new ArrayList<Double>();
	ArrayList<Double> listaMejoresGlobal = new ArrayList<Double>();
	ArrayList<Double> listaMediaGen = new ArrayList<Double>();
	// TO DO
     boolean maxim=false;
    
	
	Agenetico AG = new Agenetico(poblacion,nGen,pc,pm,0.001,maxim,false,2);
		
	AG.inicializar(precioKm); // pasamos la funci�n que es, para saber como inicializar cada individuo
	
	AG.setTamElite(AG.getTamPob() / 25); // y si se la pedimos al usuario??
	
	// transformo las aptitudes!!
	if (AG.getMaximizar()==false){ // estoy minimizando
		AG.revisarAdaptacionMinimizar();
		mejorPuntuacionGlobal = Double.MAX_VALUE;
	}
	else{
		AG.revisarAdaptacionMaximizar();
		mejorPuntuacionGlobal = - Double.MAX_VALUE;
	}
	
	AG.evaluarPoblacion();
	int generacion=0;
	while(!AG.terminado()){
		generacion++;
		//System.out.println("POS  ||          FENOTIPO       ||  APTITUD     ");
		AG.evaluarPoblacion(); // para recalcular Puntuaciones. HAY QUE PONERLO, PUES SI NO RESTOS TIENE ALGUNA PUNTUACI�N NEGATIVA
		AG.setGeneracionActual(AG.getGeneracionActual()+1);
		
		//ELITISMO
		if(elitismo==true){
			elite = AG.seleccionaElite(AG.getTamElite()); 
		}
					  
		AG.seleccion(tipofunSeleccion,presionS,generacion);
		AG.reproduccion(tipoCruce,precioKm);
		AG.mutacion(tipoMutacion);
		
		
		
		
		//ELITISMO
		if(elitismo==true){
			AG.incluye(elite);
		}
		//System.out.println("ANTES DE EVALUAR");		
		AG.evaluarPoblacion(); // para saber el mejor
		
		if (AG.getMaximizar()==false){ // estoy minimizando
			AG.revertirRevisarAdaptacionMinimizar();
		}
		else{
			AG.revertirRevisarAdaptacionMaximizar();
		}
		
		
		
		/*System.out.println("---- En la vuelta " + generacion + "tenemos la siguiente poblacion despues de todo:  ------");
		for (int i = 0; i <AG.getPob().size();i++){
			System.out.println("El individuo en la posicion " + i + " tiene una aptitud de: " + AG.getPob().get(i).getAptitud());
		}
		System.out.println("ANTES DE CAMBIOS");		*/
		// cambio el mejor de la generaci�n
		if(AG.getMaximizar()==true&& (AG.getPob().get(AG.getPos_mejor()).getAptitud()> mejorPuntuacionGlobal)){
			mejorPuntuacionGlobal=AG.getPob().get(AG.getPos_mejor()).getAptitud();
			AG.setElMejor(AG.getPob().get(AG.getPos_mejor()).copia());
		}
		if(AG.getMaximizar()==false && ( AG.getPob().get(AG.getPos_mejor()).getAptitud()<mejorPuntuacionGlobal) ){
			mejorPuntuacionGlobal=AG.getPob().get(AG.getPos_mejor()).getAptitud();
			AG.setElMejor(AG.getPob().get(AG.getPos_mejor()).copia());
		}
		
		// cojo el mejor, el mejor global hasta el momento, y la media de la generaci�n
		//listaMejoresGen.add(AG.getMejorGeneracion().getAptitud());
		listaMejoresGen.add(AG.getPob().get(AG.getPos_mejor()).getAptitud());
		listaMejoresGlobal.add(mejorPuntuacionGlobal);
		listaMediaGen.add(new Double(AG.getMediaGeneracion())); 
		
		/*System.out.println("Posicion Mejor: " + AG.getPos_mejor());
		System.out.println("Fenotipo: " + AG.getPob().get(AG.getPos_mejor()).getFenotipo());
		System.out.println("Aptitud:  " + AG.getPob().get(AG.getPos_mejor()).getAptitud());
		System.out.println("--------------------------------------------------");*/
		
		if (AG.getMaximizar()==false){ // estoy minimizando
			AG.revisarAdaptacionMinimizar();
		}
		else{
			AG.revisarAdaptacionMaximizar();
		}
		
	}
	if (AG.getMaximizar()==false){ // estoy minimizando
		AG.revertirRevisarAdaptacionMinimizar();
	}
	else{
		AG.revertirRevisarAdaptacionMaximizar();
	}
	
	/*System.out.println("�������MEJOR FINAL!!!!!!!!!");
	System.out.println("Fenotipo: " + AG.getElMejor().getFenotipo());
	System.out.println();
	System.out.println("Kilometros " + ((CromosomaCiudades)(AG.getElMejor())).kilometros());
	System.out.println("Coste " + ((CromosomaCiudades)(AG.getElMejor())).dameCosteEconomico().getH1());
	System.out.println("Dias " + ((CromosomaCiudades)(AG.getElMejor())).dameCosteEconomico().getH2());
	System.out.println("--------------------------------------------------");*/
	//Graficas g = new Graficas();
	//g.pintarGrafica("funcion",listaMejoresGen, listaMejoresGlobal, listaMediaGen);
        DatosGraficas dt = new DatosGraficas();
        dt.setElem1(listaMejoresGen);
        dt.setElem2(listaMejoresGlobal);
        dt.setElem3(listaMediaGen);
        dt.setElMejor(AG.getElMejor());
        return dt;
}

}
